package it.fuesi.expired.adapters.mongo;

import it.fuesi.expired.adapters.base.AdapterException;
import it.fuesi.expired.adapters.base.IAdapterQuery;
import it.fuesi.expired.adapters.base.IAdapterSession;
import it.fuesi.expired.model.Attachment;
import it.fuesi.expired.model.base.IModel;
import it.fuesi.expired.model.base.ModelCollection;

import java.io.IOException;
import java.net.UnknownHostException;

import org.apache.commons.io.IOUtils;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.MongoTemplate;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import com.mongodb.gridfs.GridFS;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.gridfs.GridFSInputFile;

public class MongoAdapterSession implements IAdapterSession {

	private MongoClient client;
	private DB dbConnection;
	
	private final MongoTemplate template;
	private final GridFS gridfs;
	
	private MongoAdapterSession(String host, String db) throws UnknownHostException{
		client = new MongoClient( host );
		dbConnection = client.getDB( db);
		template = new MongoTemplate(dbConnection.getMongo(), db);
		gridfs = new GridFS(dbConnection);
	}

	
	public static IAdapterSession factory(String host, String db) throws UnknownHostException {
		return new MongoAdapterSession(host, db);
	}
	
	public void close() throws AdapterException {
		client.close();
		dbConnection = null;
	}
	
	private DBCollection getCollection(Class<? extends IModel> c){
		return dbConnection.getCollection(c.getSimpleName());
	}
	

	public <T extends IModel> ModelCollection<T> getAll(Class<T> model) throws AdapterException{
		
		final ModelCollection<T> collection = new ModelCollection<T>();
		
		final DBCollection coll = getCollection(model);
	
		final DBCursor cursor = coll.find();
		
		for(DBObject obj : cursor){
			collection.add(template.getConverter().read(model, obj));
		}
		
		return collection;
		
	}


	public <T extends IModel> T put(T model) throws AdapterException{
		if(model == null) throw new AdapterException("Object is null");
		final DBCollection coll = getCollection(model.getClass());
		final DBObject obj = new BasicDBObject();
		
		template.getConverter().write(model, obj);
		coll.insert(obj);
		
		final ObjectId id = (ObjectId)obj.get( "_id" );
		if(id != null)
			model.set_id(id.toString());
		
		return model;
	}
	
	public <T extends IModel> T update(T model) throws AdapterException{
		if(model == null) throw new AdapterException("Object is null");
		final DBCollection coll = getCollection(model.getClass());
		final DBObject obj = new BasicDBObject();
		template.getConverter().write(model, obj);
		
		final DBObject filter = new BasicDBObject();
		filter.put("_id", new ObjectId(model.get_id()));
		
		coll.update(filter, obj);
		
		return model;
	}
	
	public <T extends IModel> T get(Class<T> model, String _id)
			throws AdapterException {
		if(model == null) throw new AdapterException("Model class is null");
		if(_id == null) throw new AdapterException("_id is null");
		
		final DBCollection coll = getCollection(model);
		
		final DBObject filter = new BasicDBObject();
		filter.put("_id", new ObjectId(_id));
		
		final DBObject obj = coll.findOne(filter);
		
		if(obj == null)
			throw new AdapterException("Can't find object with _id["+_id+"] on collection["+getCollection(model)+"] ");
		
		return template.getConverter().read(model, obj);
	}
	
	public void delete(Class<? extends IModel> model) throws AdapterException {
		if(model == null) throw new AdapterException("Model class is null");
		final DBCollection coll = getCollection(model);
		coll.drop();
	}
	
	public void delete(Class<? extends IModel> model, String _id)
			throws AdapterException {
		if(model == null) throw new AdapterException("Model class is null");
		if(_id == null) throw new AdapterException("_id is null");
		
		final DBCollection coll = getCollection(model);
		
		final DBObject filter = new BasicDBObject();
		filter.put("_id", new ObjectId(_id));
		
		coll.remove(filter);
	}
	
	@Override
	public <T extends IModel> ModelCollection<T> find(Class<T> model,
			IAdapterQuery<?> query) throws AdapterException {
		
		final ModelCollection<T> collection = new ModelCollection<T>();
		
		final DBCollection coll = getCollection(model);
		
		final DBCursor cursor = coll.find((DBObject)query.getQuery());
		
		System.out.println("Query: " + query.getQuery());
		
		for(DBObject obj : cursor){
			collection.add(template.getConverter().read(model, obj));
		}
		
		return collection;
		
	}
	
	@Override
	public byte[] getFile(String _id) throws AdapterException {
		
		try{
		
			final Attachment att;
			
			final GridFSDBFile file = gridfs.findOne(new ObjectId(_id));
			
			if(file != null){
				return IOUtils.toByteArray(file.getInputStream());
			}
			
		}catch(IOException e){
			throw new AdapterException(e);
		}
		
		return null;
			
	}
	
	@Override
	public Attachment putFile(Attachment att, byte[] data)
			throws AdapterException {

			final GridFSInputFile input = gridfs.createFile(data);
			input.setFilename(att.getFileName());
			input.setChunkSize(att.getFileSize());
			input.save();

			
			att.set_id(input.getId().toString());
		
		
			return att;
	}
	
	@Override
	public void deleteFile(String _id) throws AdapterException {
		gridfs.remove(new ObjectId(_id));
	}


	@Override
	public void cleanFiles() throws AdapterException {
		final DBCursor cursor = gridfs.getFileList();
		while(cursor.hasNext()){
			final DBObject obj = cursor.next();
			deleteFile(""+obj.get("_id"));
		}
		
	}
	


	
	

}
